package com.exercise.widgets.edittext

import android.content.Context
import android.graphics.drawable.Drawable
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.View.OnFocusChangeListener
import androidx.appcompat.widget.AppCompatEditText
import com.exercise.struct.utils.dp2Px

/**
 * 带清除按钮的EditText
 *
 * @param context
 * @param attrs
 * @param defStyleAttr 注意：这里使用的系统的样式，如果传0的话就没有任何样式
 */
class ClearEditText @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = android.R.attr.editTextStyle
) : AppCompatEditText(context, attrs, defStyleAttr), OnFocusChangeListener, TextWatcher {
    private var mHasFocus = false
    private var mClearDrawable: Drawable? = null

    private var mOnFocusChangeListener: OnFocusChangeListener? = null
    private var mTextWatcher: TextWatcher? = null

    init {
        mHasFocus = hasFocus()

        // compoundDrawables: 获取的时drawableRight的Drawable数组
        // compoundDrawablesRelative: 取的是drawableEnd的Drawable数组
        mClearDrawable = compoundDrawables[2] ?: compoundDrawablesRelative[2]
        mClearDrawable?.apply {
            setBounds(0, 0, intrinsicWidth, intrinsicHeight)
        }

        // 默认隐藏清除图标
        setClearIconVisible(false)
        // 设置焦点变化的监听
        setOnFocusChangeListener(this)
        // 设置输入框内容变化的监听
        addTextChangedListener(this)
    }

    /**
     * 模拟清除按钮的点击事件
     * 如果 ACTION_UP 在清除图标区域内，则认为点击了清除按钮，置空文本内容。
     *
     * @param event
     * @return
     */
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        if (event?.action == MotionEvent.ACTION_UP && mClearDrawable != null) {
            if (inClearIcon(event)) {// 点击了清除按钮，置空文本内容
                setText("")
            }
        }
        return super.onTouchEvent(event)
    }

    /**
     * 判断MotionEvent是否在清除图标区域内
     *
     * @param event
     * @return true：在清除图标区域内；false：不在清除图标区域内
     */
    private fun inClearIcon(event: MotionEvent): Boolean {
        if (mClearDrawable == null) {
            return false
        }
        // totalPaddingRight：右侧Padding，包含右侧的Drawable在内
        val clearIconLeftSide = width - totalPaddingRight - compoundDrawablePadding
        return event.x > clearIconLeftSide && event.x < width
    }

    /**
     * 输入框内容发生变化时：
     * 如果有焦点并且输入的文本内容不为空时显示清除图标
     *
     * @param text
     * @param start
     * @param lengthBefore
     * @param lengthAfter
     */
    override fun onTextChanged(
        text: CharSequence?,
        start: Int,
        lengthBefore: Int,
        lengthAfter: Int
    ) {
        setClearIconVisible(mHasFocus && !TextUtils.isEmpty(text))
        mTextWatcher?.onTextChanged(text, start, lengthBefore, lengthAfter)
    }

    /**
     * 设置清除图标的显示与隐藏
     *
     * @param visible 是否显示清除图标
     */
    private fun setClearIconVisible(visible: Boolean) {
        if (mClearDrawable == null) {
            return
        }
        val rDrawable = if (visible) {
            mClearDrawable
        } else {
            null
        }
        setCompoundDrawables(
            compoundDrawables[0],
            compoundDrawables[1],
            rDrawable,
            compoundDrawables[3]
        )
    }

    /**
     * 焦点变化时：
     * 有焦点并且输入的内容不为空时显示清除图标
     *
     * @param v
     * @param hasFocus
     */
    override fun onFocusChange(v: View?, hasFocus: Boolean) {
        this.mHasFocus = hasFocus
        // 持有焦点，且有内容时才显示清除图标
        setClearIconVisible(hasFocus && !TextUtils.isEmpty(text))
    }

    override fun setOnFocusChangeListener(l: OnFocusChangeListener?) {
        if (l is ClearEditText) {
            super.setOnFocusChangeListener(l)
        } else {
            mOnFocusChangeListener = l
        }
    }

    override fun addTextChangedListener(watcher: TextWatcher?) {
        if (watcher is ClearEditText) {
            super.addTextChangedListener(watcher)
        } else {
            mTextWatcher = watcher
        }
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        mTextWatcher?.beforeTextChanged(s, start, count, after)
    }

    override fun afterTextChanged(s: Editable?) {
        mTextWatcher?.afterTextChanged(s)
    }

}